/************************************************************************
Copyright 2003, 2004, 2005, 2006, 2007, 2008 Grid Technology Research Center,
National Institute of Advanced Industrial Science and Technology.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************/

package jp.aist.gtrc.plus.scheduler.specific.sge.status;

import java.util.Calendar;
import java.util.Collection;
import java.util.LinkedList;

import jp.aist.gtrc.plus.reserve.ReserveId;
import jp.aist.gtrc.plus.scheduler.job.JobStateType;
import jp.aist.gtrc.plus.scheduler.node.NodeAllocateSet;
import jp.aist.gtrc.plus.scheduler.specific.sge.jaxb.common.*;
import jp.aist.gtrc.plus.scheduler.status.JobID;
import jp.aist.gtrc.plus.scheduler.status.JobStatusBase;
import jp.aist.gtrc.plus.scheduler.status.NodeStatus;
import jp.aist.gtrc.plus.scheduler.util.TextUtil;
import jp.aist.gtrc.plus.scheduler.util.TimeUtil;

public class SGETaskStatus extends JobStatusBase {

	private final SGEJobStatus job;
	private final JAXBSGETask task;
	private final JobID jobId;
	private final JobStateType stateType;

	static final int TS_IDLE 			= 0x00000000;		//	0
	static final int TS_ENABLED			= 0x00000008;		//	8
	static final int TS_HELD			= 0x00000010;		// 16
	static final int TS_MIGRATING		= 0x00000020;		// 32
	static final int TS_QUEUED			= 0x00000040;		// 64
	static final int TS_RUNNING			= 0x00000080;		// 128
	static final int TS_SUSPENDED		= 0x00000100;		// 256
	static final int TS_TRANSFERING		= 0x00000200;		// 512
	static final int TS_DELETED			= 0x00000400;		// 1024
	static final int TS_WAITING			= 0x00000800;		// 2048
	static final int TS_EXITING			= 0x00001000;		// 4096
	static final int TS_WRITTEN			= 0x00002000;		// 8192
	static final int TS_WAITING4OSJID	= 0x00004000;		// 16384
	static final int TS_ERROR			= 0x00008000;		// 32768
	static final int TS_FINISHED		= 0x00010000;		// 65536
	static final int TS_SLAVE			= 0x00020000;		// 131072
	static final int TS_SIMULATED		= 0x00040000;		// 262144

	private static final SGEStatusManager statusMgr = SGEStatusManager.getInstance();
	private static final ObjectFactory objFactory = new ObjectFactory();

	SGETaskStatus(SGEJobStatus job, JAXBSGETask task) {

		assert(job != null);
		assert(task != null);
		this.job = job;
		this.task = task;
		this.jobId = new SGEJobID(job.getJobNumber(), task.getJATTaskNumber());
		this.stateType = makeStateType();

	}

	private static JAXBSGETask cloneJAXBSGETask(JAXBSGETask template,
			int tid, int status) {

		JAXBSGETask newTask = objFactory.createJAXBSGETask();

		newTask.setJATTaskNumber(tid);
		newTask.setJATStatus(status);
		newTask.setJATStartTime(template.getJATStartTime());
		newTask.setJATEndTime(template.getJATEndTime());
		newTask.setJATJobRestarted(template.isJATJobRestarted());
		newTask.setJATGrantedDestinIdentifierList(
				template.getJATGrantedDestinIdentifierList());
		newTask.setJATMasterQueue(template.getJATMasterQueue());
		newTask.setJATUsageList(template.getJATUsageList());

		return newTask;

	}

	static SGETaskStatus getDummyTask(SGEJobStatus job, int tid, int status) {

		JAXBSGETask task = cloneJAXBSGETask(job.getTemplate(), tid, status);
		SGETaskStatus newStatus = new SGETaskStatus(job, task);
		return newStatus;

	}

	int getTaskNumber() {

		return task.getJATTaskNumber();

	}

	public SGEJobStatus getSGEJobStatus() {

		return job;

	}

	public JobID getJobID() {

		return jobId;

	}

	public String getOwner() {

		return job.getOwner();

	}

	public int getUid() {

		return job.getUid();

	}

	public int getPriority() {

		return job.getPriority();

	}

	public void setPriority(int newPrio) {

		job.setPriority(newPrio);

	}

	public String getName() {

		return getJobID().toString();

	}

	public Calendar getSubmitTime() {

		return job.getSubmitTime();

	}

	public Calendar getExecStartTime() {

		int sec = task.getJATStartTime();
		return (sec > 0) ? TimeUtil.makeCalendarFromSec(sec) : null;

	}

	public Calendar getRequestedExecStartTime() {

		return job.getRequestedExecStartTime();

	}

	public ReserveId getReserveId() {

		return job.getReserveId();

	}

	public Collection<NodeAllocateSet> getNodeRequests() {

		return job.getNodeRequests();

	}

	public int getRequestedTime() {

		return job.getRequestedTime();

	}

	public String getOwnersGroup() {

		return job.getOwnersGroup();

	}

	public int getGid() {

		return job.getGid();

	}

	public boolean isRunnableOn(NodeStatus node) {

		return job.isRunnableOn(node);

	}

	private JobStateType makeStateType() {

		// 'state' is bit-or of TaskStatusType value
		int state = task.getJATStatus();

		if ((state & TS_HELD) != 0)
			return JobStateType.Held;
		if ((state & TS_SUSPENDED) != 0)
			return JobStateType.Suspended;
		if ((state & TS_FINISHED) != 0)
			return JobStateType.Exiting;
		if ((state & TS_DELETED) != 0)
			return JobStateType.Exiting;
		if ((state & TS_RUNNING) != 0)
			return JobStateType.Running;

		Calendar now = Calendar.getInstance();
		Calendar reqT = getRequestedExecStartTime();
		if ((reqT != null) && now.before(reqT))
			return JobStateType.Held;

		return JobStateType.Queued;

	}

	public JobStateType getState() {

		return stateType;

	}

	public static int getSGEStateValue(String state) {

		if (TextUtil.isEmpty(state)) {
			return TS_FINISHED;
		}

		int v = 0;
		if (state.contains("h")) {
			v |= TS_HELD;
		}
		if (state.contains("d")) {
			v |= TS_DELETED;
		}
		if (state.contains("s") || state.equals("suspended")) {
			v |= TS_SUSPENDED;
		}
		if (state.equals("qw") || state.equals("pending")) {
			// NOTE: "pending" means "qw", "h", "hr"
			v |= TS_QUEUED;
		}
		if (state.equals("r") || state.equals("running")) {
			v |= TS_RUNNING;
		}

		return v;

	}

	public boolean isReadyToRun() {

		if (statusMgr.isDirtyTask((SGEJobID)getJobID()))
			return false;

		/*
		 *	JB_restart		JAT_job_restarted 	stauts	readyToRun
		 *	-------------------------------------------------------------
		 *	0 or 2			0						IDLE	YES
		 *	0 or 2			1					<nerver happen>
		 *
		 *	1 (yes)			0						IDLE	YES
		 *	1				1						IDLE	YES
		 */

		// except held/suspended/waiting even if the job is queued
		return (task.getJATStatus() == TS_IDLE);

	}

	public boolean isReadyToDelete() {

		return (getState() == JobStateType.Exiting);

	}

	public boolean isQueued() {

		return (getState() == JobStateType.Queued);

	}

	public boolean isRunning() {

		return (getState() == JobStateType.Running);

	}

	public String[] getTargetQueueNames() {

		return job.getTargetQueueNames();

	}

	public String getTargetProjectName() {

		return job.getTargetProjectName();

	}

	public	String getTargetParallelEnvName() {

		return job.getTargetParallelEnvName();

	}

	public SGEParallelEnv getTargetParallelEnv() {

		return job.getTargetParallelEnv();

	}

	public int[] getPEProcNumRange() {

		return job.getPEProcNumRange();

	}

	public Collection<JGElement> getGrantedQueues() {

		JATGrantedDestinIdentifierList list =
			task.getJATGrantedDestinIdentifierList();
		if (list != null) {
			return list.getJGElement();
		} else {
			return new LinkedList<JGElement>();
		}

	}

	public Collection<UAElement> getUsage() {

		JATUsageList list = task.getJATUsageList();
		if (list != null) {
			return list.getUAElement();
		} else {
			return new LinkedList<UAElement>();
		}

	}

	public String toString() {

		return task.toString();

	}

}
